
{#
  Top-level rendering of a model.Property.
#}
{% macro renderTopProperty(prop, prefix) %}

  {% set type = prop.type %}

<div class="type stack">
  <h3 id="{{ prefix }}-{{ prop.name }}">{{ prop.name }}</h3>

{# Render the function signature. #}
{% if type.key === 'function' %}
  {{ renderTypeSignature(prop) }}
{% elif type.key === 'ref' and type.name === 'events.Event' %}
  {% if type.templates[0].primitiveType === 'void' %}
    {# This is a declarative event listener as it has no listener function type. #}
    <p>
      Provides the <a href="/docs/extensions/reference/events/#declarative-event-handlers">Declarative Event API</a> consisting of <code>addRules</code>, <code>removeRules</code>, and <code>getRules</code>.
    </p>
  {% else %}
    {# This isn't really part of the type but we show users how to add a listener. #}
    <div class="type--label">
      <code>
        {{ prop.name }}.addListener(listener: <span class="color-cyan-medium">function</span>)
      </code>
    </div>
  {% endif %}
{% endif %}

  {{ renderComment(prop) }}
</div>

{{ renderTopType(type, prop) }}

{% endmacro %}


{#
  Top-level rendering of a model.Type. This is needed as we recurse for union types.
#}
{% macro renderTopType(type, prop) %}

  {% if not type %}
    {# This should probably never happen. #}

  {% elif prop.isType and type.key === 'choices' %}
    {# Type that contains many string (probably) options, basically an enum. #}
    {{ renderOptionalTypesArray(type.choices, 'Enum', prop) }}

  {% elif type.key === 'function' %}
    {# Top-level namespace function. #}

    {% if type.isReturnsAsync %}
      {{ renderOptionalParametersSection(type.parametersForAsync, 'Parameters') }}

      <div class="code-sections">
        <h4 class="type--label code-sections__mode">Result</h4>

        <ul class="stack">
          <li class="stack">
            <div>
              <div class="code-sections__label">return/callback</div>
              <div class="type--xsmall code-sections__icon code-sections__icon--function">async</span></div>
            </div>

            {% set returnParameters = type.callbackForAsync.type.parameters %}

            <div class="type stack">
              {{ renderComment(type.callbackForAsync) }}
              <p>
                The <code>{{ prop.name }}</code> method provides its result via callback or returned as a <code>Promise</code> (MV3 only).
                {% if not returnParameters.length %}
                  It has no parameters.
                {% endif %}
              </p>
            </div>

            {{ renderOptionalParametersSection(returnParameters) }}
          </li>
        </ul>
      </div>

    {% else %}
      {{ renderOptionalParametersSection(type.parameters, 'Parameters') }}
      {% if type.returnProperty and type.returnType.primitiveType !== 'void' %}
        <div class="code-sections">
          <h4 class="type--label code-sections__mode">Returns</h4>

          <ul class="stack">
            {{ internalRenderParameter(type.returnProperty) }}
          </ul>
        </div>
      {% endif %}
    {% endif %}

  {% elif type.key === 'ref' and type.name === 'events.Event' %}
    {# Top-level event variable. #}

    {% if type.templates[0].primitiveType === 'void' %}
      {{ renderOptionalTypesArray(type.templates[1].choices, 'Conditions', prop) }}
      {{ renderOptionalTypesArray(type.templates[2].choices, 'Actions', prop) }}
    {% else %}
      <div class="code-sections">
        <h4 class="type--label code-sections__mode">Event</h4>
        <ul class="stack">
          {{ internalRenderListenerAsItem(type.templates[0]) }}
        </ul>
      </div>
    {% endif %}

  {% elif type.key === 'literal' %}
    {# Top-level literal number or string. #}
    <div class="code-sections">
      <h4 class="type--label code-sections__mode">Value</h4>

      <div class="code-sections__overline code-sections__label">
        {{ renderSingleType(type, prop) }}
      </div>
    </div>

  {% elif type.key === 'union' and not prop.isType %}
    {# Union of many things as a non-type. #}

    {% for type in type.types %}
      {{ renderTopType(type, prop) }}
    {% endfor %}

  {% elif type.key === 'object' %}
    {# Object with properties. #}

    {% if type.properties | length %}
      <div class="code-sections">
        <h4 class="type--label code-sections__mode">Properties</h4>

        <ul class="stack">
          {% for name, prop in type.properties | dictsort %}
            {{ internalRenderParameter(prop) }}
          {% endfor %}
        </ul>
      </div>
    {% endif %}

  {% else %}
    {# Some other kind of type. #}

    <div class="code-sections">
      <h4 class="type--label code-sections__mode">Type</h4>

      <div class="code-sections__overline code-sections__label">
        {{ renderSingleType(type, prop) }}
      </div>
    </div>

  {% endif %}

{% endmacro %}

{#
  Renders an optional list of parameters.
#}
{% macro renderOptionalParametersSection(parameters, label) %}

  {% if parameters.length %}
    <div class="code-sections">
      {% if label %}
        <h4 class="type--label code-sections__mode">{{ label }}</h4>
      {% endif %}

      <ul class="stack">
        {% for parameter in parameters %}
          {{ internalRenderParameter(parameter) }}
        {% endfor %}
      </ul>
    </div>
  {% endif %}

{% endmacro %}

{#
  Renders an optional list of types, e.g., for an enum.
#}
{% macro renderOptionalTypesArray(types, label, prop) %}

  {% if types.length %}
    <div class="code-sections">
      {% if label %}
        <h4 class="type--label code-sections__mode">{{ label }}</h4>
      {% endif %}

      <div class="code-sections__overline code-sections__label">
        <p>
          {% for t in types %}
            {% if loop.last and not loop.first %}or{% endif %}
            {{ renderSingleType(t, prop) | trim }}{% if not loop.last %},{% endif %}
          {% endfor %}
        </p>
      </div>
    </div>
  {% endif %}

{% endmacro %}

{#
  Renders a comment, its optional version information and deprecation notice.
#}
{% macro renderComment(prop) %}
  {{ renderAvailabilityFeature(prop.feature, prop) }}
  {% if prop.deprecated %}
    <p class="code-sections__deprecated">{{ prop.deprecated | mdInline | safe }}<p>
  {% endif %}
  {# This |md filter will add <p> as appropriate. #}
  {{ prop.description | md | safe }}
{% endmacro %}

{#
  Renders a <li> based on a callback/listener model.FunctionType. Used by events on top-level and
  as part of interfaces.
#}
{% macro internalRenderListenerAsItem(listenerType, name = 'listener') %}
  <li class="stack">
    <div>
      <div class="code-sections__label">{{ name }}</div>
      <div class="type--xsmall code-sections__icon code-sections__icon--function">function</span></div>
    </div>

    <div class="type stack">
      <p>
        The {{ name }} parameter should be a function that looks like this:
      </p>
    </div>

    <div class="code-sections__callback type--xsmall"><code>{{ renderFunctionParameters(listenerType.parameters) }} => {...}</code></div>

    {{ renderOptionalParametersSection(listenerType.parameters) }}
  </li>
{% endmacro %}

{#
  Renders a <li> with the type's name and description. This is used for parts of a top-level type.
#}
{% macro internalRenderParameter(prop) %}
  {% set type = prop.type %}

<li class="stack">
  <div>
    <div class="code-sections__label">{{ prop.name }}</div>
    <div class="type--xsmall">{{ renderSingleType(prop.type, prop) }}</div>
  </div>

{% set description %}
  {{ renderComment(prop) }}

  {% if type.key === 'function' and (prop.name === 'callback' or prop.name === 'listener') %}
    {# This is a callback or a listener. Tell the user how to specify it. #}
    <p>
      {% if prop.optional %}
        If you specify the {{ spec.name }} parameter, it
      {% else %}
        The {{ spec.name }} parameter
      {% endif %}
      should be a function that looks like this:
    </p>
    {% set functionPart %}
      {{ renderFunctionParameters(type.parameters) }} => {...}
    {% endset %}
  {% elif type.key === 'function' %}
    {# This is a function, often given to the user. It's ambiguous how it will
       be called, so just show its signature. #}
    <p>
      The {{ prop.name }} function looks like this:
    </p>
    {% set functionPart %}
      {{ prop.name }}{{ renderFunctionParameters(type.parameters) }} => {...}
    {% endset %}
  {% endif %}
{% endset %}

{% set description = description | trim %}
{% if description %}
  <div class="type stack code-sections__description">{{ description | safe }}</div>
{% endif %}

{% if functionPart %}
  <div class="code-sections__callback type--xsmall"><code>{{ functionPart | safe }}</code></div>
{% endif %}

{# OPTION: array #}
{% if type.key === 'sequence' %}
  {# Render further information about the contained type. #}
  {% set type = type.itemType %}
{% endif %}

{# OPTION: type (class) or object (property) #}
{% if type.key === 'object' %}
  {% if type.properties | length %}
    <div class="code-sections">
      <ul class="stack">
        {% for name, prop in type.properties | dictsort %}
          {{ internalRenderParameter(prop) }}
        {% endfor %}
      </ul>
    </div>
  {% endif %}

{# OPTION: function #}
{% elif type.key === 'function' %}
  {% if type.parameters.length or type.returnType.primitiveType !== 'void' %}
    <div class="code-sections">
      <ul class="stack">
        {% for parameter in type.parameters %}
          {{ internalRenderParameter(parameter) }}
        {% endfor %}
        {% if type.returnType.primitiveType !== 'void' %}
          {{ internalRenderParameter(type.returnProperty) }}
        {% endif %}
      </ul>
    </div>
  {% endif %}

{# OPTION: event #}
{% elif type.key === 'ref' and type.name === 'events.Event' %}
  <div class="code-sections">
    <ul class="stack">
      {{ internalRenderListenerAsItem(type.templates[0]) }}
    </ul>
  </div>

{% endif %}

</li>
{% endmacro %}

{#
  Renders a single model.Type as a single word. Used for signatures and type hints.

  If outerProp is non-null, shows a hint icon, because it's the left-most type inside a table view.
#}
{% macro renderSingleType(type, outerProp) %}
  {% set iconType = type.key %}

  {% set stringType %}
    {% if not type %}
      {# Should never happen #}
      ?

    {% elif type.key === 'primitive' %}
      {# Primitive type, e.g. "number" or "void" #}
      {% set iconType = type.primitiveType %}
      {{ type.primitiveType }}

    {% elif type.key === 'literal' %}
      {# Literal value, e.g., 123 or '"foo"' #}
      {% set iconType = type.primitiveType %}
      <span class="code-sections__value">{{ type.value | dump }}</span>

    {% elif type.key === 'ref' %}
      {# Reference to another type (internal or external) #}

      {% set iconType = 'object' %}

      {% set link = type.name | modelToHref %}
      {% if link %}
        <a href="{{ link }}">{{ type.name }}</a>
      {%- else %}
        {{ type.name }}
      {%- endif %}

      {%- if type.templates.length -%}
        &lt;
          {%- for t in type.templates -%}
            {{ renderSingleType(t) | trim }}
          {%- endfor -%}
        &gt;
      {% endif %}

    {% elif type.key === 'sequence' %}
      {# An array of something else. #}
      {{ renderSingleType(type.itemType) | trim }}[]

    {% elif type.key === 'choices' %}
      {# An number of choices. #}
      {%- for t in type.choices -%}
        {{ renderSingleType(t) | trim }}{%- if not loop.last %} | {% endif -%}
      {%- endfor -%}

    {% else %}
     {# This is a fallback display but won't render a sensible type that looks like TS. #}
     {{ type.key }}
    {% endif %}

  {% endset %}

  {# Note that we avoid whitespace below, and instead set margins in CSS. #}
  {% if outerProp %}
    <span class="code-sections__icon code-sections__icon--{{ iconType }}">{{ stringType | safe | trim }}
      {%- if outerProp.optional -%}
        &nbsp;<span class="code-sections__optional">optional</span>
      {%- endif -%}
    </span>
  {% else %}
    <span>{{ stringType | safe | trim }}</span>
  {% endif %}

{% endmacro %}

{#
  If the passed model.Property is a function, then render its full signature. Does nothing otherwise.
#}
{% macro renderTypeSignature(prop) %}

  {% if prop.type.key === 'function' %}
    {% set type = prop.type %}
    <div class="type--label">
      {% if type.isReturnsAsync %}
        <code>
          {{ prop.name }}{{ renderFunctionParameters(type.parametersForAsync) }}:
          {{ renderSingleType(type.returnTypeForAsync) }}
        </code><br />
      {% endif %}
      <code>
        {{ prop.name }}{{ renderFunctionParameters(type.parameters) }}:
        {{ renderSingleType(type.returnType) }}
      </code>
    </div>
  {% endif %}

{% endmacro %}

{#
  Renders the arguments of a method including ()'s. This is passed as model.Property[].
#}
{% macro renderFunctionParameters(parameters) -%}
  (
    {%- for parameter in parameters -%}
      {{ parameter.name }}{% if parameter.optional %}?{% endif %}:
      <span class="color-cyan-medium">{{ renderSingleType(parameter.type) | trim }}</span>
      {%- if not loop.last %}, {% endif -%}
    {%- endfor -%}
  )
{%- endmacro %}

{#
  Renders a short summary of the passed model.Property[]. Used at the top of a namespace page for
  different sections (types, events, etc).
#}
{% macro renderSummarySection(namespaceName, label, prefix, array) %}

  {% if array.length %}
    <li>
      <div class="code-sections__label">{{ label }}</div>
      <div>
        {% for prop in array %}
          <div><a href="#{{ prefix }}-{{ prop.name }}" class="link weight-medium">{{ prop.name }}</a></div>
          {{ renderTypeSignature(prop) }}
        {% endfor %}
      </div>
    </li>
  {% endif %}

{% endmacro %}

{#
  Renders the whole list of the passed model.Property[].
#}
{% macro renderPrimarySection(namespaceName, id, label, prefix, array) %}

  {% if array.length %}
    <h2 class="type--h3" id="{{ id }}">{{ label }}</h2>
    <div class="stack flow-space-300">
    {% for prop in array %}
      {{ renderTopProperty(prop, prefix) }}
    {% endfor %}
    </div>
  {% endif %}

{% endmacro %}

{#
  Renders API availability information based on a sparse Feature. This is used for both top-level
  namespaces as well as specific APIs. This does not render permissions.

  This returns valid HTML, so it should be rendered with `| safe`.
#}
{% macro renderAvailabilityFeature(feature, prop) %}

  {% set inner %}
    {% if prop.type.isReturnsAsync %}
      <span class="aside--success tag-pill type--label rounded-lg" title="Can return its result via Promise">
        Promise
      </span>
    {% endif %}

    {% if feature.supportedInChannel === 'dev' %}
      <span class="aside--warning tag-pill type--label rounded-lg">
        Dev channel
      </span>
    {% elif feature.supportedInChannel === 'beta' %}
      <span class="aside--warning tag-pill type--label rounded-lg">
        Beta channel
      </span>
    {% elif feature.supportedInChannel === 'canary' or feature.supportedInChannel === 'trunk' %}
      <span class="aside--warning tag-pill type--label rounded-lg">
        In development
      </span>
    {% elif feature.unknownVersion %}
      {# This is stable but not yet visible in the version history data. It's probably coming next
        Chrome release. #}
      <span class="aside--warning tag-pill type--label rounded-lg">
        Unknown version
      </span>
    {% elif feature.availableFromVersion %}
      <span class="aside--default tag-pill type--label rounded-lg">
        Chrome {{ feature.availableFromVersion }}+
      </span>
    {% endif %}

    {% if feature.maxManifestVersion %}
      <span class="aside--caution tag-pill type--label rounded-lg" title="Maximum manifest vesion">
        Manifest V{{ feature.maxManifestVersion }}
      </span>
    {% endif %}
    {% if feature.minManifestVersion %}
      <span class="aside--gotchas tag-pill type--label rounded-lg">
        Manifest V{{ feature.minManifestVersion }}+
      </span>
    {% endif %}

    {% if feature.disallowForServiceWorkers %}
      <span class="aside--gotchas tag-pill type--label rounded-lg" title="Not available in Service Workers">
        Foreground only
      </span>
    {% endif %}

    {% if prop.deprecated !== undefined or feature.deprecatedSinceVersion %}
      <span class="aside--warning tag-pill type--label rounded-lg">
        Deprecated
        {%- if feature.deprecatedSinceVersion > 0 %}
          {{ feature.deprecatedSinceVersion }}+
        {%- endif -%}
      </span>
    {% endif %}
  {% endset %}

  {% if inner | trim %}
    <div class="display-flex flex-flow-row-wrap">{{ inner | trim | safe }}</div>
  {% endif %}

{% endmacro %}